#include "intlwidget.h"
#include <QFileDialog>
#include <QDir>
#include <QDebug>
#include <QJsonDocument>
#include <QHBoxLayout>
#include <QMessageBox>
#include <vector>
#include <unordered_set>
#include <algorithm>
#include <QNetworkReply>
IntlWidget::IntlWidget(QWidget *parent) : QWidget(parent)
{
  container = new QVBoxLayout(this);
  buttonSelecctIntlDir = new QPushButton(this);
  buttonSelecctIntlDir->setText("Select Intl Directory");
  container->addWidget(buttonSelecctIntlDir);
  labelIntlSelected = new QLabel(this);
  container->addWidget(labelIntlSelected);
  labelIntlSelected->setText("null");

  connect(buttonSelecctIntlDir, &QPushButton::pressed, this,
          &IntlWidget::onSelectIntlDirClicked);

  QHBoxLayout *layoutConfig = new QHBoxLayout();
  buttonSelectConfig = new QPushButton(this);
  buttonSelectConfig->setText("Select Config");
  layoutConfig->addWidget(buttonSelectConfig);

  labelConfig = new QLabel(this);
  labelConfig->setText("");
  layoutConfig->addWidget(labelConfig);

  buttonWriteConfig = new QPushButton(this);
  buttonWriteConfig->setText("Write");
  layoutConfig->addWidget(buttonWriteConfig);
  connect(buttonWriteConfig, &QPushButton::pressed, this, &IntlWidget::onConfigWriteClicked);
  container->addLayout(layoutConfig);

  QHBoxLayout *layoutInsert = new QHBoxLayout();
  buttonInsert = new QPushButton(this);
  buttonInsert->setText("generate");
  connect(buttonInsert, &QPushButton::pressed, this, &IntlWidget::onInsertClicked);
  lineEditInsertZH = new QLineEdit(this);
  lineEditInsertEN = new QLineEdit(this);
  layoutInsert->addWidget(lineEditInsertZH, 1);
  layoutInsert->addWidget(lineEditInsertEN, 1);
  layoutInsert->addWidget(buttonInsert);
  textEditInsertResult = new QTextEdit(this);
  textEditInsertResult->setMinimumHeight(100);
  container->addLayout(layoutInsert);
  container->addWidget(textEditInsertResult);

  container->addStretch();

  network = new QNetworkAccessManager(this);

  connect(buttonSelectConfig, &QPushButton::pressed, this,
          &IntlWidget::onSelectConfigClicked);
}

void IntlWidget::onSelectConfigClicked()
{

  QString fileName = QFileDialog::getOpenFileName(this,
                                                  tr("Select Config"), QString(), tr("Text files (*.txt);;"));

  if (fileName.isEmpty())
  {
    return;
  }
  QFile file(fileName);
  if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
  {
    QMessageBox::warning(this, "warning", "open failed");
    return;
  }

  configs.clear();

  QTextStream in(&file);
  size_t line = 0;
  bool failed = false;
  using Map = QHash<QString, LanguageConfig>;
  Map filted;
  if (in.atEnd())
  {
    failed = true;
  }
  else
  {
    do
    {
      QString zh = in.readLine();
      ++line;
      if (in.atEnd())
      {
        failed = true;
        break;
      }
      QString en = in.readLine();
      ++line;
      filted[zh] = LanguageConfig(zh, en);

    } while (!in.atEnd());
  }
  if (failed)
  {
    qDebug() << "failed:";
    labelConfig->setText(QString("failed line:%1").arg(line + 1));
  }
  else
  {
    auto begin = filted.constBegin();
    while (begin != filted.constEnd())
    {
      configs.push_back(begin.value());
      ++begin;
    }

    size_t size = line / 2;
    qDebug() << "success:";
    labelConfig->setText(QString("success length:%1,filter:%2").arg(configs.size()).arg(size - filted.size()));
  }
}

inline bool writeText(const QFileInfo &fileInfo, const QByteArray &data)
{
  QFile file(fileInfo.filePath());
  if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
  {
    return false;
  }

  QTextStream out(&file);
  out << data;
  return true;
}

LanguageConfig IntlWidget::insertToCaches(const LanguageConfig &config)
{
  LanguageConfig configPlaceholder = replaceLaunagePlaceholder(config);
  const QString &zh = std::get<0>(config);
  QString pinyinKey = generatePinyinKey(caches, zh.mid(0, std::min<size_t>(zh.size(), 20)));
  caches.insert(pinyinKey, configPlaceholder);
  return configPlaceholder;
}

void IntlWidget::onConfigWriteClicked()
{
  // CacheMap caches = {{"nihao",{"你好","hello"}},{"nihao_1",{"你好1","hello1"}},{"nihao_3",{"你好3","hello3"}},{"nihao_4",{"你好4","hello4"}}};
  // QString pinyinKey = generatePinyinKey(caches,"不好XXXXXX");
  // qDebug()<<"pinyinKey:"<<pinyinKey;
  for (auto &config : configs)
  {
    // qDebug() << "zh:" << zh;
    LanguageConfig configPlaceholder = replaceLaunagePlaceholder(config);
    const QString &zh = std::get<0>(config);
    QString pinyinKey = generatePinyinKey(caches, zh.mid(0, std::min<size_t>(zh.size(), 20)));
    caches.insert(pinyinKey, configPlaceholder);
    // qDebug() << "pinyinKey:" << pinyinKey;
  }
  QJsonObject zhJson;
  QJsonObject enJson;

  for (auto start = caches.cbegin(); start != caches.cend(); ++start)
  {
    auto &key = start.key();
    const auto &value = start.value();
    zhJson.insert(key, std::get<0>(value));
    enJson.insert(key, std::get<1>(value));
    qDebug() << start.key() << ":" << std::get<0>(value) << "|" << std::get<1>(value);
  }

  QJsonDocument zhDocument(zhJson);
  QJsonDocument enDocument(enJson);

  auto zhString = zhDocument.toJson(QJsonDocument::JsonFormat::Indented);
  auto enString = enDocument.toJson(QJsonDocument::JsonFormat::Indented);

  QFileDialog dialog(this);
  dialog.setFileMode(QFileDialog::Directory);
  QStringList fileNames;

  if (dialog.exec())
  {
    QJsonObject zh;
    QJsonObject en;
    fileNames = dialog.selectedFiles();

    QFileInfo zhFileInfo(fileNames[0], "zh.txt");
    if (!writeText(zhFileInfo, zhString))
    {
      QMessageBox::warning(this, "warning", "genereate zh failed");
      return;
    }

    QFileInfo enFileInfo(fileNames[0], "en.txt");
    if (!writeText(enFileInfo, enString))
    {
      QMessageBox::warning(this, "warning", "genereate en failed");
      return;
    }
    QMessageBox::information(this, "infomation", "generate successful");
  }
  // QString result = replaceAll("XXXXXX参加了NNNNNN次运动,XXXXXX参加了NNNNNN次运动");
  // qDebug() << "result:" << result;
}

void IntlWidget::onSelectIntlDirClicked()
{
  QFileDialog dialog(this);
  dialog.setFileMode(QFileDialog::Directory);
  QStringList fileNames;

  if (dialog.exec())
  {
    QJsonObject zh;
    QJsonObject en;
    fileNames = dialog.selectedFiles();
    QDir dir(fileNames[0]+"/lib/l10n");
    QFileInfoList fileList = dir.entryInfoList();
    for (int i = 0; i < fileList.size(); ++i)
    {
      QFileInfo fileInfo = fileList.at(i);
      if (fileInfo.isFile() && fileInfo.completeSuffix() == "arb")
      {
        if (fileInfo.fileName().contains("zh"))
        {
          QFile f = QFile(fileInfo.filePath());
          f.open(QIODevice::ReadWrite | QIODevice::Text);
          zh = QJsonDocument::fromJson(f.readAll()).object();
          std::get<0>(intlFiles)=fileInfo.filePath();
        }
        else if (fileInfo.fileName().contains("en"))
        {
          QFile f = QFile(fileInfo.filePath());
          f.open(QIODevice::ReadWrite | QIODevice::Text);
          en = QJsonDocument::fromJson(f.readAll()).object();
          std::get<1>(intlFiles)=fileInfo.filePath();
        }
      }
    }
    if (zh.isEmpty() || en.isEmpty())
    {
      QMessageBox::warning(this, "warning", "empty");
      return;
    }
    this->caches.clear();

    auto zhBegin = zh.constBegin();
    while (zhBegin != zh.constEnd())
    {
      if (zhBegin->isString())
      {
        this->caches[zhBegin.key()] = LanguageConfig(zhBegin->toString(), "");
      }
      ++zhBegin;
    }

    auto enBegin = en.constBegin();
    while (enBegin != en.constEnd())
    {
      const auto k = enBegin.key();
      auto ki = this->caches.find(k);
      if (ki == this->caches.end())
      {
        this->caches[k] = LanguageConfig("", enBegin->toString());
      }
      else
      {
        std::get<1>(ki.value()) = enBegin->toString();
      }

      ++enBegin;
    }

    updateIntlCount();
    QMessageBox::information(this, "information", "success");
  }
}

void IntlWidget::updateIntlCount()
{
   labelIntlSelected->setText(QString("zh,en,count:%1").arg(this->caches.size()));
}

void IntlWidget::onInsertClicked()
{
  QString zhInput = this->lineEditInsertZH->text();
  QString enInput = this->lineEditInsertEN->text();
  if (zhInput.isEmpty())
  {
    textEditInsertResult->clear();
    return;
  }
  if (enInput.isEmpty())
  {
    enInput = zhInput;
  }
  LanguageConfig config(zhInput, enInput);
  LanguageConfig configPlaceholder = replaceLaunagePlaceholder(config);
  for (auto begin = caches.cbegin(); begin != caches.cend(); ++begin)
  {
    if (std::get<0>(begin.value()) == std::get<0>(configPlaceholder))
    {
      lineEditInsertZH->clear();
      lineEditInsertEN->clear();
      QMessageBox::information(this,"information","exist");
      return;
    }
  }
  buttonInsert->setEnabled(false);
  const QString &zh = replaceKeyPlaceholder(std::get<0>(config));
    requestPinYin(network,zh,[=,configPlaceholder = std::move(configPlaceholder)](const QString &pinyin){
        QString pinyinKey = generatePinyinKey(caches, pinyin);
        caches.insert(pinyinKey, configPlaceholder);
        if(rewrite())
        {
            updateIntlCount();
            QString j;
            j.append('"').append(pinyinKey).append('"').append(':').append(' ').append('"').append(std::get<0>(configPlaceholder)).append('"');
            j.append('\n');
            j.append('"').append(pinyinKey).append('"').append(':').append(' ').append('"').append(std::get<1>(configPlaceholder)).append('"');
            textEditInsertResult->setText(j);
        }
        else
        {
            caches.remove(pinyinKey);
            QMessageBox::warning(this,"warning","rewrite failed");
        }

        lineEditInsertZH->clear();
        lineEditInsertEN->clear();
        buttonInsert->setEnabled(true);
    },[=](const QString &errorMessage){
       QMessageBox::warning(this,"warning",errorMessage) ;
       buttonInsert->setEnabled(true);
    });
}

QString IntlWidget::replaceKeyPlaceholder(const QString &origin)
{
  QString result = origin;

  size_t zhN = 0;
  qsizetype zhIndex;
  while ((zhIndex = result.indexOf(kStringPlaceholder)) >= 0)
  {
    result.replace(zhIndex, kStringPlaceholder.length(), "S");
    ++zhN;
  }

  size_t enN = 0;
  qsizetype enIndex;
  while ((enIndex = result.indexOf(kNumPlaceholder)) >= 0)
  {
    result.replace(enIndex, kNumPlaceholder.length(), "N");
    ++enN;
  }
  return result;
}

QString IntlWidget::replaceValuePlaceholder(const QString &origin)
{
  QString result = origin;

  size_t zhN = 0;
  qsizetype zhIndex;
  while ((zhIndex = result.indexOf(kStringPlaceholder)) >= 0)
  {
    result.replace(zhIndex, kStringPlaceholder.length(), QString("{S%1}").arg(zhN));
    ++zhN;
  }

  size_t enN = 0;
  qsizetype enIndex;
  while ((enIndex = result.indexOf(kNumPlaceholder)) >= 0)
  {
    result.replace(enIndex, kNumPlaceholder.length(), QString("{N%1}").arg(enN));
    ++enN;
  }
  return result;
}

LanguageConfig IntlWidget::replaceLaunagePlaceholder(const LanguageConfig &config)
{
  QString zh = replaceValuePlaceholder(std::get<0>(config));
  QString en = replaceValuePlaceholder(std::get<1>(config));
  return LanguageConfig(std::move(zh), std::move(en));
}

QString IntlWidget::generatePinyinKey(const CacheMap &caches,const QString &pinyinReplaceHolder)
{
  auto py = generatePinyin(pinyinReplaceHolder);
  auto pinyin = py.mid(0, std::min<size_t>(py.size(), kPinyinKeyMaxLength));
  auto begin = caches.cbegin();
  // QMap<size_t, QString> list;
  std::unordered_set<size_t> numSet;
  while (begin != caches.cend())
  {
    const QString &k = begin.key();
    if (k.startsWith(pinyin) && (k.size() == pinyin.size() || k[pinyin.size()] == kSplitChar))
    {
      //未命名||名称的后一位是kSplitChar，才可以进行计数
      QString end = k.mid(pinyin.size(), k.size() - pinyin.size());
      if (end.isEmpty())
      {
        // list.insert(0, "");
        numSet.insert(0);
      }
      else
      {
        size_t num = end.mid(kSplitChar.size(), end.size() - kSplitChar.size()).toUInt();
        numSet.insert(num);
      }
    }
    ++begin;
  }

  if (numSet.size() == 0)
  {
    return pinyin;
  }
  else
  {
    std::vector numList(numSet.cbegin(), numSet.cend());
    std::sort(numList.begin(), numList.end());
    size_t n = 0;
    for (size_t i = 0; i < numList.size(); ++i, ++n)
    {
      if (n != numList[i])
      {
        pinyin.append(kSplitChar).append(QString::number(n));
        return pinyin;
      }
    }
    pinyin.append(kSplitChar).append(QString::number(n));
    return pinyin;
  }
}

IntlWidget::~IntlWidget()
{
}

bool IntlWidget::rewrite() {
    QJsonObject zhJson;
    QJsonObject enJson;

    for (auto start = caches.cbegin(); start != caches.cend(); ++start)
    {
        auto &key = start.key();
        const auto &value = start.value();
        zhJson.insert(key, std::get<0>(value));
        enJson.insert(key, std::get<1>(value));
        qDebug() << start.key() << ":" << std::get<0>(value) << "|" << std::get<1>(value);
    }

    QJsonDocument zhDocument(zhJson);
    QJsonDocument enDocument(enJson);

    auto zhString = zhDocument.toJson(QJsonDocument::JsonFormat::Indented);
    auto enString = enDocument.toJson(QJsonDocument::JsonFormat::Indented);

    QFileDialog dialog(this);
    dialog.setFileMode(QFileDialog::Directory);
    QStringList fileNames;
    QFileInfo zhFileInfo(std::get<0>(intlFiles));
    if (!writeText(zhFileInfo, zhString))
    {
        return false;
    }

    QFileInfo enFileInfo(std::get<1>(intlFiles));
    if (!writeText(enFileInfo, enString))
    {
        return false;
    }
    return true;
}
